using System;
using System.Collections.Generic;
using NPack.Interfaces;

namespace NPack.Matrix
{
    public class AffineTransformMatrix<T> : AffineMatrix<T>, IAffineTransformMatrix<T>
        where T : struct, IEquatable<T>, IComparable<T>, IComputable<Double, T>, IConvertible, IFormattable
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="format"></param>
        /// <param name="rank"></param>
        public AffineTransformMatrix(MatrixFormat format, int rank)
            : base(format, rank)
        {
        }

        private AffineTransformMatrix(Matrix<T> copyFrom)
            : base(copyFrom)
        {
        }

        private AffineTransformMatrix(MatrixFormat format, int rank, T value)
            : base(format, rank)
        {
            for (Int32 i = 0; i < rank; i++)
            {
                _elements[i*RowCount + i] = value;
            }
        }

        public new AffineTransformMatrix<T> Inverse
        {
            get { return new AffineTransformMatrix<T>(base.Inverse); }
        }

        #region IAffineTransformMatrix<T> Members

        /// <summary>
        /// Compares the current object with another object of the same type.
        /// </summary>
        /// <returns>
        /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: 
        ///                     Value 
        ///                     Meaning 
        ///                     Less than zero 
        ///                     This object is less than the <paramref name="other"/> parameter.
        ///                     Zero 
        ///                     This object is equal to <paramref name="other"/>. 
        ///                     Greater than zero 
        ///                     This object is greater than <paramref name="other"/>. 
        /// </returns>
        /// <param name="other">An object to compare with this object.
        ///                 </param>
        public int CompareTo(IMatrix<T> other)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the difference of the Object and <paramref name="b"/>.
        /// It must not modify the value of the Object.
        /// </summary>
        /// <param name="b">The second operand.</param>
        /// <returns>The difference.</returns>
        public IMatrix<T> Subtract(IMatrix<T> b)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the sum of the Object and <paramref name="b"/>.
        /// It must not modify the value of the Object.
        /// </summary>
        /// <param name="b">The second operand.</param>
        /// <returns>The sum.</returns>
        public IMatrix<T> Add(IMatrix<T> b)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the quotient of the Object and <paramref name="b"/>.
        /// It must not modify the value of the Object.
        /// </summary>
        /// <param name="b">The second operand.</param>
        /// <returns>The quotient.</returns>
        public IMatrix<T> Divide(IMatrix<T> b)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the product of the Object and <paramref name="b"/>.
        /// It must not modify the value of the Object.
        /// </summary>
        /// <param name="b">The second operand.</param>
        /// <returns>The product.</returns>
        public IMatrix<T> Multiply(IMatrix<T> b)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns the multiplicative identity.
        /// </summary>
        /// <value>e</value>
        public IMatrix<T> One
        {
            get { return Identity(Format, RowCount); }
        }

        /// <summary>
        /// Returns true if the value called on is strictly greater than the <paramref name="value"/> given.
        /// </summary>
        /// <param name="value">The value to compare to.</param>
        /// <returns>True if the called on instance is greater, false if the <paramref name="value"/> is greater or equal.</returns>
        public bool GreaterThan(IMatrix<T> value)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns true if the value called on is greater than or equal to the <paramref name="value"/> given.
        /// </summary>
        /// <param name="value">The value to compare to.</param>
        /// <returns>True if the called on instance is greater or equal, false if the <paramref name="value"/> is greater.</returns>
        public bool GreaterThanOrEqualTo(IMatrix<T> value)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns true if the value called on is strictly less than the <paramref name="value"/> given.
        /// </summary>
        /// <param name="value">The value to compare to.</param>
        /// <returns>True if the called on instance is less, false if the <paramref name="value"/> is less or equal.</returns>
        public bool LessThan(IMatrix<T> value)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Returns true if the value called on is less than or equal to the <paramref name="value"/> given.
        /// </summary>
        /// <param name="value">The value to compare to.</param>
        /// <returns>True if the called on instance is less or equal, false if the <paramref name="value"/> is less.</returns>
        public bool LessThanOrEqualTo(IMatrix<T> value)
        {
            throw new NotImplementedException();
        }


        /// <summary>
        /// Gets the inverse of the matrix, if one exists.
        /// </summary>
        IMatrix<T> IMatrix<T>.Inverse
        {
            get { return Inverse; }
        }


        /// <summary>
        /// Rotates the affine transform around the given <paramref name="axis"/> 
        /// at the given <paramref name="point"/>.
        /// </summary>
        /// <param name="point">Point at which to compute the rotation.</param>
        /// <param name="axis">The axis to rotate around.</param>
        /// <param name="radians">Angle to rotate through.</param>
        public void RotateAt(IVector<T> point, IVector<T> axis, double radians)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Rotates the affine transform around the given <paramref name="axis"/> 
        /// at the given <paramref name="point"/>.
        /// </summary>
        /// <param name="point">Point at which to compute the rotation.</param>
        /// <param name="axis">The axis to rotate around.</param>
        /// <param name="radians">Angle to rotate through.</param>
        /// <param name="order">Order in which to apply the operation.</param>
        public void RotateAt(IVector<T> point, IVector<T> axis, double radians, MatrixOperationOrder order)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Translates the affine transform by the given translation vector.
        /// </summary>
        /// <param name="translateVector">
        /// A vector whose components will translate the transform 
        /// in the corresponding dimension.
        /// </param>
        public void Translate(IVector<T> translateVector)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Translates the affine transform by the given translation vector.
        /// </summary>
        /// <param name="translateVector">
        /// A vector whose components will translate the transform 
        /// in the corresponding dimension.
        /// </param>
        /// <param name="order">Order in which to apply the operation.</param>
        public void Translate(IVector<T> translateVector, MatrixOperationOrder order)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Gets the inverse of the affine transform.
        /// </summary>
        IAffineTransformMatrix<T> IAffineTransformMatrix<T>.Inverse
        {
            get { return Inverse; }
        }


        /// <summary>
        /// Scales the matrix by the given vector <paramref name="scaleVector"/>.
        /// </summary>
        /// <param name="scaleVector">
        /// A vector with scaling components which 
        /// correspond to the affine transform dimensions.
        /// </param>
        public void Scale(IVector<T> scaleVector)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Scales the matrix by the given vector <paramref name="scaleVector"/>.
        /// </summary>
        /// <param name="scaleVector">
        /// A vector with scaling components which 
        /// correspond to the affine transform dimensions.
        /// </param>
        /// <param name="order">Order in which to apply the operation.</param>
        public void Scale(IVector<T> scaleVector, MatrixOperationOrder order)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Applies a shear to the transform.
        /// </summary>
        /// <param name="shearVector">The vector used to compute the shear.</param>
        public void Shear(IVector<T> shearVector)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Applies a shear to the transform.
        /// </summary>
        /// <param name="shearVector">The vector used to compute the shear.</param>
        /// <param name="order">Order in which to apply the operation.</param>
        public void Shear(IVector<T> shearVector, MatrixOperationOrder order)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Rotates the affine transform around the given <paramref name="axis"/>.
        /// </summary>
        /// <param name="axis">
        /// The axis to rotate around. May be an addition of the basis vectors.
        /// </param>
        /// <param name="radians">Angle to rotate through.</param>
        public void RotateAlong(IVector<T> axis, double radians)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Rotates the affine transform around the given <paramref name="axis"/>.
        /// </summary>
        /// <param name="axis">
        /// The axis to rotate around. May be an addition of the basis vectors.
        /// </param>
        /// <param name="radians">Angle to rotate through.</param>
        /// <param name="order">Order in which to apply the operation.</param>
        public void RotateAlong(IVector<T> axis, double radians, MatrixOperationOrder order)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Applies this transform to the given <paramref name="input"/> matrix.
        /// </summary>
        /// <param name="input">Matrix to transform.</param>
        /// <returns>
        /// The multiplication of this transform matrix with the input matrix.
        /// </returns>
        public IMatrix<T> TransformMatrix(IMatrix<T> input)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Applies this transform to the given <paramref name="input"/> vector.
        /// </summary>
        /// <param name="input">Vector to transform.</param>
        /// <returns>
        /// The multiplication of this transform matrix with the input vector.
        /// </returns>
        public IVector<T> TransformVector(IVector<T> input)
        {
            return base.TransformVector(new Vector<T>(input));
        }

        /// <summary>
        /// Applies this transform to the given <paramref name="input"/> vectors.
        /// </summary>
        /// <param name="input">Set of vectors to transform.</param>
        /// <returns>
        /// The multiplication of this transform matrix with each of the input vectors.
        /// </returns>
        public IEnumerable<IVector<T>> TransformVectors(IEnumerable<IVector<T>> input)
        {
            foreach (IVector<T> vector in input)
            {
                yield return TransformVector(vector);
            }
            ;
        }

        public new IMatrix<T> Clone()
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> GetMatrix(int[] rowIndexes, int startColumn, int endColumn)
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> Transpose()
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> Abs()
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> Set(double value)
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> Negative()
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> Zero
        {
            get { throw new NotImplementedException(); }
        }

        public new IMatrix<T> Power(double exponent)
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> Sqrt()
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> Log(double newBase)
        {
            throw new NotImplementedException();
        }

        public new IMatrix<T> Exp()
        {
            throw new NotImplementedException();
        }

        #endregion

        public new AffineTransformMatrix<T> Identity(MatrixFormat format, Int32 rank)
        {
            return new AffineTransformMatrix<T>(format, rank, default(T).One);
        }
    }
}